package com.kmx.demo.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

/**
 * @author zhengchunxi at 2016年1月18日 下午5:24:10
 *
 */
public class DateUtils {

	/**
	 * 将格式为给定的format的time转成Date类型
	 * @param time : string类型的时间
	 * @param format : 参数time的格式
	 * @return
	 */
	public static Date toDate(String time, String format) {
		Date date = null;
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		try {
			date = sdf.parse(time);
		} catch (ParseException e) {
			throw new IllegalArgumentException("input time:" + time + " can't not be formated to format:" + format);
		}
		return date;
	}

	public static String toDate(long timestamp, String format) {
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		return sdf.format(new Date(timestamp));
	}

	/**
	 * 将Date类型的日期转化成iso标准格式
	 * @param date : 日期
	 * @return
	 */
	public static String toISO(Date date) {
		return new DateTime(date).toDateTime().toString();
	}

	/**
	 * 将long类型(timestamp时间搓)转化成iso标准格式
	 * @param times
	 * @return
	 */
	public static String convertTimestampToISO(long times) {
		DateTime dateTime = new DateTime(times);
		return dateTime.toDateTime().toString();
	}

	public static String toISO(long times) {
		return convertTimestampToISO(times);
	}

	public static long convertIsoToTimestamp(String iso) {
		DateTime dateTime = DateTime.parse(iso);
		return dateTime.getMillis();
	}

	public static String toISO_UTC(long time) {
		DateTime dtDateTime = new DateTime(time);
		return dtDateTime.toDateTime(DateTimeZone.UTC).toString();
	}

	public static String toISO_UTC(String time, String format) {
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		Date date = null;
		try {
			date = sdf.parse(time);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		DateTime dtDateTime = new DateTime(date);
		return dtDateTime.toDateTime(DateTimeZone.UTC).toString();
	}

	public static String toISOWithTimeZone(long timestamp, int zone) {

		DateTime dtDateTime = new DateTime(new Date(timestamp));
		return dtDateTime.toDateTime(DateTimeZone.forOffsetHours(zone)).toString();

	}

	public static String toISOWithTimeZone(String time, DateTimeZone zone, String format) {

		SimpleDateFormat sdf = new SimpleDateFormat(format);
		Date date = null;
		try {
			date = sdf.parse(time);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		DateTime dtDateTime = new DateTime(date);
		return dtDateTime.toDateTime(zone).toString();

	}

	public static String toISOWithTimeZone(String time, DateTimeZone zone) {

		return toISOWithTimeZone(time, zone, "yyyyMMddHHmmssSSS");

	}

	public static String toISO8(long timestamp) {
		DateTime dtDateTime = new DateTime(new Date(timestamp));
		return dtDateTime.toDateTime(DateTimeZone.forOffsetHours(8)).toString();
	}

	public static String toISO8(String time, String format) {
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		Date date = null;
		try {
			date = sdf.parse(time);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		DateTime dtDateTime = new DateTime(date);
		return dtDateTime.toDateTime(DateTimeZone.forOffsetHours(8)).toString();
	}

	/**
	 * 将格式为yyyyMMddhhmmssSSS的时间转化成iso标准格式
	 * @param time： 格式为yyyyMMddhhmmssSSS的时间
	 * @return
	 */
	public static String toISO_UTC(String time) {
		return toISO_UTC(time, "yyyyMMddHHmmssSSS");
	}

	public static String toDateTime(long time) {
		DateTime dtDateTime = new DateTime(time);
		return dtDateTime.toString("yyyyMMddHHmmssSSS");
	}

	/**
	 * 将格式为给定的format的time转成iso标准格式
	 * @param time:  string类型的时间
	 * @param format : 参数time的格式
	 * @return
	 */
	public static String toISO(String time, String format) {
		Date date = toDate(time, format);
		return toISO(date);
	}

	/**
	 * 将格式为format的时间转化成timestamp时间搓格式(long类型)
	 * @param time:  string类型的时间
	 * @param format: 参数time的格式
	 * @return
	 */
	public static long toTimestamp(String time, String format) {
		Date date = toDate(time, format);
		return date.getTime();
	}

	/**
	 * 将格式为yyyyMMddhhmmssSSS的时间转化成timestamp时间搓格式(String类型)
	 * @param time ： 格式为yyyyMMddhhmmssSSS的时间
	 * @return
	 */
	public static Long toTimestamp(String time) {
		return toTimestamp(time, "yyyyMMddHHmmssSSS");
	}

	public static String toAuditDate(long time) {
		DateTime dtDateTime = new DateTime(time);
		return dtDateTime.toString("yyyy-MM-dd HH:mm:ss");
	}

	/***
	 * 将date按照format格式成字符串
	 * @param date
	 * @param format
	 * @return
	 */
	public static String getDate(Date date, String format) {
		return new SimpleDateFormat(format).format(date);
	}

	public static String getDate(long timestamp, String format) {
		return new SimpleDateFormat(format).format(new Date(timestamp));
	}

	public static long paserDurationToTimeMillis(String duration) {
		long timeMillis = 0;
		String unit = duration.substring(duration.length() - 1, duration.length()).toLowerCase();
		long dur = Long.valueOf(duration.replace(unit, "").trim());
		if ("d".equals(unit)) {
			timeMillis = dur * 24 * 60 * 60 * 1000;
		} else if ("h".equals(unit)) {
			timeMillis = dur * 60 * 60 * 1000;
		} else if ("m".equals(unit)) {
			timeMillis = dur * 60 * 1000;
		} else if ("s".equals(unit)) {
			timeMillis = dur * 1000;
		} else if ("ms".equals(unit)) {
			timeMillis = dur;
		}
		return timeMillis;
	}
	
	/**
	 * Split time range with interval, [ ... ]
	 * @param start		ISO
	 * @param end		ISO
	 * @param interval		ms
	 * @return
	 */
	public static Object[] timeSplit(String start, String end, long interval, boolean uniform){
		long startTime = convertIsoToTimestamp(start);
		long endTime = convertIsoToTimestamp(end);
		
		if(endTime < startTime){
			return null;
		}
		
		if(uniform){
			if(startTime % interval != 0){
				startTime = (startTime / interval + 1) * interval;
			}
			
			if(endTime % interval != 0){
				endTime = (endTime / interval) * interval;
			}
		}
		
		long count = (endTime - startTime) / interval + 1;		
		Object[] times = new Object[(int)count];
		for(int i = 0; i < count; i++){
			long time = startTime + i * interval;
			times[i] = convertTimestampToISO(time);
		}			
		return times;
	}
	
	/**
	 * Split time range with interval, [ ... )
	 * @param start
	 * @param end
	 * @param unit
	 * @return
	 */
	public static Object[] aggregationSplit(String start, String end, String unit){
		Object[] times = null;
		
		long startTime = convertIsoToTimestamp(start);
		long endTime = convertIsoToTimestamp(end);
		
		Date startDate = new Date(startTime);
		Date endDate = new Date(endTime);
		
		Date exactStart = exactTime(startDate, unit, true).getTime();
		Date exactEnd = exactTime(endDate, unit, false).getTime();
		
		startTime = exactStart.getTime();
		endTime = exactEnd.getTime();
		
		if(endTime >= startTime){
			int count = difference(exactStart, exactEnd, unit);
			times = new Object[count];
			for(int i = 0; i < count; i++){
				Date time = plusTime(exactStart, unit, i);
				times[i] = toISO(time);
			}
		}
		
		return times;
	}
	
	/**
	 * 整段时间 (hour, day, week, month, year)
	 * @param date
	 * @param unit
	 * @param next	下一个/上一个
	 * @return	整段时间的起点
	 */
	public static Calendar exactTime(Date date, String unit, boolean next){
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		
		int month = calendar.get(Calendar.MONTH);
		int day_of_month = calendar.get(Calendar.DAY_OF_MONTH);
		int day_of_week = calendar.get(Calendar.DAY_OF_WEEK);
		int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY);
		int minute = calendar.get(Calendar.MINUTE);
		int second = calendar.get(Calendar.SECOND);
		int millisecond = calendar.get(Calendar.MILLISECOND);
		
		if(unit.equalsIgnoreCase("1h")){	
			if(minute != 0 || second != 00 ||  millisecond != 0){
				if(next){
					calendar.add(Calendar.HOUR_OF_DAY, 1);
				}
			}		
		}else if(unit.equalsIgnoreCase("1d")){
			if(minute != 0 || second != 00 ||  millisecond != 0 || hourOfDay != 0){
				if(next){
					calendar.add(Calendar.DAY_OF_MONTH, 1);
				}
			}
			calendar.set(Calendar.HOUR_OF_DAY, 0);			
		}else if(unit.equalsIgnoreCase("1w")){
			if(minute != 0 || second != 00 ||  millisecond != 0 || hourOfDay != 0 || day_of_week != Calendar.MONDAY){
				if(next){
					calendar.add(Calendar.WEEK_OF_YEAR, 1);
				}
			}
			calendar.set(Calendar.HOUR_OF_DAY, 0);			
			calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);			
		}else if(unit.equalsIgnoreCase("1mm")){
			if(minute != 0 || second != 00 ||  millisecond != 0 || hourOfDay != 0 || day_of_month != 0){
				if(next){
					calendar.add(Calendar.MONTH, 1);
				}
			}
			calendar.set(Calendar.HOUR_OF_DAY, 0);	
			calendar.set(Calendar.DAY_OF_MONTH, 1);	
		}else if(unit.equalsIgnoreCase("1y")){
			if(minute != 0 || second != 00 ||  millisecond != 0 || hourOfDay != 0 || day_of_month != 0 || month != 0){
				if(next){
					calendar.add(Calendar.YEAR, 1);
				}
			}
			calendar.set(Calendar.HOUR_OF_DAY, 0);	
			calendar.set(Calendar.DAY_OF_MONTH, 1);	
			calendar.set(Calendar.MONTH, 0);	
		}else{
			//do nothing
		}
		calendar.set(Calendar.MILLISECOND, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MINUTE, 0);		

		return calendar;
	}
	
	/**
	 * difference of times
	 * @param start
	 * @param end
	 * @param unit
	 * @return
	 */
	public static int difference(Date start, Date end, String unit){
		int count = 0;
		Calendar startCalendar = Calendar.getInstance();
		startCalendar.setTime(start);
		long startTime = start.getTime();
				
		Calendar endCalendar = Calendar.getInstance();
		endCalendar.setTime(end);
		long endTime = end.getTime();
		
		if(start.getTime() <= end.getTime()){
			if(unit.endsWith("h")){
				long hour = 60 * 60 * 1000;
				count = (int) ((endTime - startTime) / hour);
			}
			
			if(unit.endsWith("d")){
				long day = 24 * 60 * 60 * 1000;
				count =  (int) ((endTime - startTime) / day);
			}
			
			if(unit.endsWith("w")){
				int weeks = 0;
				
				while(startCalendar.getTime().before(end)){
					startCalendar.add(Calendar.WEEK_OF_YEAR, 1);
					weeks++;
				}
				
				count = weeks;
			}
			
			if(unit.endsWith("mm")){
				int months = 0;
				while(startCalendar.getTime().before(end)){
					startCalendar.add(Calendar.MONTH, 1);
					months++;
				}
						
				count = months;
			}
			
			if(unit.endsWith("y")){
				int startYear= startCalendar.get(Calendar.YEAR);
				int endYear = endCalendar.get(Calendar.YEAR);			
				count = endYear - startYear;
			}		
		}	
		return count;
	}
	
	/**
	 * plus time
	 * @param date
	 * @param unit
	 * @param amount
	 * @return
	 */
	public static Date plusTime(Date date, String unit, int amount){
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		
		if(unit.endsWith("h")){
			calendar.add(Calendar.HOUR_OF_DAY, amount);
		}
		
		if(unit.endsWith("d")){
			calendar.add(Calendar.DAY_OF_MONTH, amount);
		}
		
		if(unit.endsWith("w")){
			calendar.add(Calendar.WEEK_OF_YEAR, amount);
		}
		
		if(unit.endsWith("m")){
			calendar.add(Calendar.MONTH, amount);
		}
		
		if(unit.endsWith("y")){
			calendar.add(Calendar.YEAR, amount);
		}
		
		return calendar.getTime();
	}		
	
	/**
	 * plus time in ISO
	 * @param iso
	 * @param unit
	 * @param amount
	 * @return
	 */
	public static String plusTime(String iso, String unit, int amount){		
		long ts = convertIsoToTimestamp(iso);
		Date date = new Date(ts);
		
		Date result = plusTime(date, unit, amount);
		return toISO(result);
	}		
	
	/**
	 * plus iso with miliSeconds
	 * @param time
	 * @param miliSeconds
	 * @return
	 */
	public static String plusISO(String time, int miliSeconds){
		long result = convertIsoToTimestamp(time) + miliSeconds;	
		return convertTimestampToISO(result);
	}
	

	public static long convertDurationToMs(String duration) {
		String unit = "x";
		Pattern p = Pattern.compile("(\\d+)");
		Matcher m = p.matcher(duration);
		long data = 0;
		if (m.find()) {
			data = Long.parseLong(m.group(1));
		}
		unit = duration.replace(data + "", "");
		switch (unit) {
		case "h":
			return 3600000 * data;
		case "m":
			return 60000 * data;
		case "d":
			return 3600000 * 24 * data;
		default:
			System.out.println("Duration's unit not in [m(minute),h(hour),d(day)], will use the default value 'm'");
			return 60000 * data;
		}

	}

	
	
	
	/**
	 * Test
	 * @param args
	 */
	public static void main(String[] args) {
		String startISO = "2015-12-08T01:12:34.567+08:00";
    	String endISO = "2017-12-09T00:21:56.789+08:00";

    	Object[] times = aggregationSplit(startISO, endISO, "1y");
    	for(Object time : times){
    		System.out.println(time.toString());
    	}

	}
}
